﻿// Decompiled with JetBrains decompiler
// Type: Netick.EntityMetaReplicator
// Assembly: Netick, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
// MVID: 6726ECA1-C773-4CF7-8952-E81B30D93B52
// Assembly location: D:\WorkSpaceDemo\UPM\NetickForUnity\Netick\Runtime\Netick.dll
// XML documentation location: D:\WorkSpaceDemo\UPM\NetickForUnity\Netick\Runtime\Netick.xml

using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

#nullable disable
namespace Netick;

internal sealed class EntityMetaReplicator : ReplicatorComponent
{
  internal HashSet<int> ChangedElements;
  private Stack<int> _pool;
  private readonly int OffsetToGridPosition;
  private readonly int OffsetToInputSourceId;
  private readonly int UserMetaSizeWords;
  private readonly int NetickMetaSizeWords;
  private readonly int ItemSizeWords;

  public EntityMetaReplicator(NetickEngine engine)
    : base(engine)
  {
    this.UserMetaSizeWords = engine._gameEngine.GetEntityMetaSizeWords();
    this.OffsetToGridPosition = NetickUtils.GetWordSize(Marshal.OffsetOf(typeof (NetickEntityMeta), "GroupIndex").ToInt32());
    this.OffsetToInputSourceId = NetickUtils.GetWordSize(Marshal.OffsetOf(typeof (NetickEntityMeta), "InputSourceId").ToInt32());
    this.NetickMetaSizeWords = NetickUtils.GetWordSize(sizeof (NetickEntityMeta));
    this.ItemSizeWords = NetickUtils.GetWordSize(sizeof (NetickEntityMeta)) + engine._gameEngine.GetEntityMetaSizeWords();
  }

  public override unsafe void Reset()
  {
    this.ChangedElements = new HashSet<int>(this.Engine.Config.MaxObjects);
    this._pool = new Stack<int>(this.Engine.Config.MaxObjects);
    for (int index = 0; index < this.Engine.Config.MaxObjects; ++index)
    {
      this.GetEntityMeta(index)->StateSizeWords = -1;
      this.GetEntityMeta(index)->Id = -1;
      this.GetEntityMeta(index)->GroupIndex = -1;
      this.Push(index);
    }
  }

  public override int GetSizeWords() => this.Engine.Config.MaxObjects * this.ItemSizeWords;

  public override unsafe void PostReadPhase1()
  {
    IGameEngine gameEngine = this.Engine._gameEngine;
    foreach (int changedElement in this.ChangedElements)
      gameEngine.OnEntityMetaChanged1(*this.GetEntityMeta(changedElement), this.GetEntityUserMeta(changedElement), changedElement);
    foreach (int changedElement in this.ChangedElements)
      gameEngine.OnEntityMetaChangedPhase2(*this.GetEntityMeta(changedElement), this.GetEntityUserMeta(changedElement), changedElement);
  }

  [MethodImpl(MethodImplOptions.AggressiveInlining)]
  public unsafe int AddEntity(Entity entity)
  {
    int worldIndex = entity.WorldIndex;
    int rawId = entity.RawId;
    this.GetEntityMeta(worldIndex)->Id = rawId;
    this.GetEntityMeta(worldIndex)->StateSizeWords = (int) entity.StateSizeWords;
    this.GetEntityMeta(worldIndex)->InputSourceId = entity.InternalInputSourceIndex;
    this.SetEntityMetaDirty(worldIndex);
    return worldIndex;
  }

  public unsafe void RemoveEntity(Entity entity)
  {
    int worldIndex = entity.WorldIndex;
    if (worldIndex == -1 || this.GetEntityMeta(worldIndex)->StateSizeWords == -1)
      return;
    this.SetEntityMetaDirty(entity.WorldIndex);
    this.GetEntityMeta(worldIndex)->StateSizeWords = -1;
    this.GetEntityMeta(worldIndex)->GroupIndex = -1;
    this.SetEntityMetaDirty(worldIndex);
    this.ResetUserData(worldIndex);
    this.Push(worldIndex);
  }

  [MethodImpl(MethodImplOptions.AggressiveInlining)]
  public void OnElementRead(int dataIndex)
  {
    this.ChangedElements.Add(dataIndex / this.ItemSizeWords);
  }

  public void Push(int id) => this._pool.Push(id);

  public int Pop() => this._pool.Pop();

  public unsafe byte* GetEntityUserMeta(int index) => (byte*) (this.GetEntityMeta(index) + 1);

  public unsafe NetickEntityMeta* GetEntityMeta(int index)
  {
    return (NetickEntityMeta*) (this.StatePtr + index * this.ItemSizeWords);
  }

  public unsafe void SetInterestGroup(Entity entity, int groupIndex)
  {
    if (entity.WorldIndex == -1)
      return;
    int worldIndex = entity.WorldIndex;
    this.GetEntityMeta(worldIndex)->GroupIndex = groupIndex;
    this.MarkAltered(this.StatePtr + (this.OffsetToGridPosition + worldIndex * this.ItemSizeWords));
  }

  [MethodImpl(MethodImplOptions.AggressiveInlining)]
  internal unsafe void SetInputSource(int index, int inputSourceIndex)
  {
    if (index == -1)
      return;
    this.GetEntityMeta(index)->InputSourceId = inputSourceIndex;
    this.MarkAltered(this.StatePtr + (this.OffsetToInputSourceId + index * this.ItemSizeWords));
  }

  public unsafe void SetEntityUserMetaDirty(int itemIndex, int* userData)
  {
    int* statePtr = this.StatePtr;
    int num = itemIndex * this.ItemSizeWords;
    for (int index = 0; index < this.UserMetaSizeWords; ++index)
    {
      int* p = statePtr + num + this.NetickMetaSizeWords + index;
      if (userData[index] != *p)
        this.MarkAltered(p);
      *p = userData[index];
    }
  }

  private unsafe void ResetUserData(int itemIndex)
  {
    int* statePtr = this.StatePtr;
    int num = itemIndex * this.ItemSizeWords;
    for (int index = 0; index < this.UserMetaSizeWords; ++index)
    {
      int* p = statePtr + num + this.NetickMetaSizeWords + index;
      if (*p != 0)
        this.MarkAltered(p);
      *p = 0;
    }
  }

  public unsafe void SetEntityMetaDirty(int index)
  {
    int* statePtr = this.StatePtr;
    int num = index * this.ItemSizeWords;
    for (int index1 = 0; index1 < this.NetickMetaSizeWords; ++index1)
      this.MarkAltered(statePtr + num + index1);
  }
}
